/**
  Copyright (c) [2024] [JiangNan]
  [pandas] is licensed under Mulan PSL v2.
  You can use this software according to the terms and conditions of the Mulan PSL v2.
  You may obtain a copy of Mulan PSL v2 at:
           http://license.coscl.org.cn/MulanPSL2
  THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
  See the Mulan PSL v2 for more details.
*/

package series

import (
	"cmp"
	"fmt"
	"github.com/shopspring/decimal"
	"math"
	"reflect"
	"slices"
	"sort"
	"strings"
)

type Series struct {
	Name     string
	elements []Element
	t        Type
	indexes  []int
}

type Type string

const (
	String Type = "string"
	Int    Type = "int"
	Float  Type = "float64"
	Bool   Type = "bool"
)

// NewSeries 创建数据列
//
//	values: 数据切片
//	dType: 数据类型，可选String、Int、float64、Bool
//	name: 数据列名称
func NewSeries[S interface{ ~[]E }, E int | float64 | string | bool](values S, dType Type, name string) (Series, error) {
	s := Series{
		Name:     name,
		t:        dType,
		elements: NewElements(dType, 0),
	}

	// 检查是否为 nil 切片
	if any(values) == nil {
		return s, nil
	}

	// 获取实际元素类型并验证是否匹配 dType
	var elem E
	if !checkTypeMatch(elem, dType) {
		return s, fmt.Errorf("输入切片与指定数据类型不匹配")
	}

	if err := s.Append(values); err != nil {
		return s, err
	}

	return s, nil
}

// checkTypeMatch 根据值的类型与 Type 枚举做匹配
func checkTypeMatch[E int | float64 | string | bool](v E, t Type) bool {
	switch any(v).(type) {
	case int:
		return t == Int
	case float64:
		return t == Float
	case string:
		return t == String
	case bool:
		return t == Bool
	default:
		return false
	}
}

// LoadRecords 用字符串切片创建指定类型数据列
//
//	values: 数据切片
//	dType: 数据类型，可选String、Int、float64、Bool
//	name: 数据列名称
func LoadRecords(values []string, t Type, name string) Series {
	ns := Series{
		Name:     name,
		elements: NewElements(t, len(values)),
		t:        t,
	}
	for i, value := range values {
		ns.Element(i).Set(value)
	}
	ns.InitIndex()
	return ns
}

// InitIndex 重置索引
func (s *Series) InitIndex() {
	s.indexes = make([]int, 0, s.Len())
	for i := 0; i < s.Len(); i++ {
		s.indexes = append(s.indexes, i)
	}
}

// Append 向数据集后添加元素,可以为单个元素或元素切片,最好保证数据类型一致
//
//	values：Series、int []int、string []string ...
func (s *Series) Append(values any) error {
	if values == nil {
		return fmt.Errorf("不允许添加 nil 值")
	}

	v := reflect.ValueOf(values)
	kind := v.Kind()

	// 处理切片类型
	if kind == reflect.Slice || kind == reflect.Array {
		for i := 0; i < v.Len(); i++ {
			elem := v.Index(i).Interface()
			if err := s.Append(elem); err != nil {
				return err
			}
		}
		s.InitIndex()
		return nil
	}

	// 处理单个值
	var x Element
	switch val := values.(type) {
	case int:
		x = new(intElement)
		x.Set(val)
	case float64:
		x = new(floatElement)
		x.Set(val)
	case string:
		x = new(stringElement)
		x.Set(val)
	case bool:
		x = new(boolElement)
		x.Set(val)
	case Element:
		x = val
	case Series:
		s.elements = append(s.elements, val.elements...)
	default:
		return fmt.Errorf("不支持的数据类型 %T", val)
	}

	if x != nil {
		s.elements = append(s.elements, x.copy())
	}

	s.InitIndex()
	return nil
}

// Drop 删除指定索引的元素
func (s *Series) Drop(indexes ...int) Series {
	// 去重
	seen := make(map[int]struct{})
	var uniqueIndexes []int
	for _, idx := range indexes {
		if _, ok := seen[idx]; !ok && idx >= 0 && idx < len(s.elements) {
			uniqueIndexes = append(uniqueIndexes, idx)
			seen[idx] = struct{}{}
		}
	}

	// 排序并倒序
	slices.SortFunc(uniqueIndexes, func(i, j int) int {
		return cmp.Compare(j, i)
	})

	ns := s.Copy()
	for _, index := range uniqueIndexes {
		ns.elements = slices.Delete(ns.elements, index, index+1)
		ns.indexes = slices.Delete(ns.indexes, index, index+1)
	}
	return ns
}

// Concat 将新数据集加原数据集后,如果类型不同，以原数据集为准
func (s *Series) Concat(x Series) error {
	// 创建副本以避免修改原始输入 x
	xCopy := x.Copy()

	if xCopy.t != s.t {
		if err := xCopy.SetType(s.t); err != nil {
			return err
		}
	}

	s.elements = slices.Concat(s.elements, xCopy.elements)
	s.InitIndex()

	return nil
}

// SubSet 保留对应索引的元素
func (s *Series) SubSet(indexes ...int) (*Series, error) {
	if len(indexes) == 0 {
		return nil, fmt.Errorf("indexes is empty")
	}

	if slices.Min(indexes) < 0 || slices.Max(indexes) >= len(s.elements) {
		return nil, fmt.Errorf("index out of range")
	}

	// 去重（可选，根据业务需求启用）
	// indexes = slices.Compact(slices.Clone(indexes))

	elements := make([]Element, 0, len(indexes))
	for _, index := range indexes {
		elements = append(elements, s.elements[index].copy())
	}

	newSeries := Series{
		Name:     s.Name,
		elements: elements,
		t:        s.t,
		indexes:  indexes,
	}

	return &newSeries, nil
}

// Elements 返回数据集元素对象切片
func (s *Series) Elements() []Element {
	return s.elements
}

// Element 指定索引元素
func (s *Series) Element(i int) Element {
	return s.elements[i]
}

// Format 批量处理数据集
//
//	index: 元素索引
//	elem: 元素对象
func (s *Series) Format(f func(index int, elem Element) Element) {
	for i, element := range s.elements {
		s.elements[i].update(f(i, element))
	}
}

// SetType 改变数据集类型
func (s *Series) SetType(t Type) error {
	if s.t == t {
		return nil
	}

	// 获取当前值前先判断类型是否合法
	var values any
	switch t {
	case String:
		values = s.Records()
	case Int:
		values = s.Int()
	case Float:
		values = s.Float()
	case Bool:
		values = s.Bool()
	default:
		return fmt.Errorf("未知数据类型！")
	}

	// 验证 values 是否有效
	if values == nil {
		return fmt.Errorf("无法转换类型：原始数据为空")
	}

	s.elements = NewElements(t, 0)
	if err := s.Append(values); err != nil {
		return err
	}
	s.t = t

	return nil
}

// SortIndex 排序
func (s *Series) SortIndex(reverse bool) {
	sort.SliceStable(s.elements, func(i, j int) bool {
		a, b := s.elements[i], s.elements[j]
		if reverse {
			a, b = b, a
		}

		switch s.t {
		case String:
			return cmp.Compare(a.Records(), b.Records()) < 0
		case Int:
			return cmp.Compare(a.Int(), b.Int()) < 0
		case Float:
			return cmp.Compare(a.Float(), b.Float()) < 0
		default:
			return false
		}
	})
}

// 自定义输出
func (s *Series) String() string {
	return fmt.Sprintf("字段名：%s\n数 据：%v\n索 引：%v\n类 型：%s\n", s.Name, s.Records(), s.indexes, s.t)
}

// Len 返回数据集大小
func (s *Series) Len() int {
	return len(s.elements)
}

// Type 返回类型
func (s *Series) Type() string {
	return string(s.t)
}
func (s *Series) DType() Type {
	return s.t
}

// HasNaN 判断是否存在空值
func (s *Series) HasNaN() bool {
	for _, element := range s.elements {
		if element.isNaN() {
			return true
		}
	}
	return false
}

// Copy 复制
func (s *Series) Copy() Series {
	// 防止 Len() 异常或 elements 为 nil 导致 NewElements 出错
	length := s.Len()
	if length < 0 {
		length = 0 // 或者根据业务需求做更严格的处理
	}

	// 深拷贝 indexes（假设是 []int 类型）
	var indexesCopy []int
	if s.indexes != nil {
		indexesCopy = make([]int, len(s.indexes))
		copy(indexesCopy, s.indexes)
	}

	ns := Series{
		Name:     s.Name,
		elements: NewElements(s.t, length),
		t:        s.t,
		indexes:  indexesCopy,
	}

	if s.elements != nil {
		for i, element := range s.elements {
			ns.elements[i].update(element)
		}
	}

	return ns
}

// Records 将数据集中的元素作为字符串返回
func (s *Series) Records() []string {
	var x []string
	for _, element := range s.elements {
		x = append(x, element.Records())
	}
	return x
}

// Float 将数据集中的元素作为浮点数返回，如果数据转换失败自动设为 math.NaN()
func (s *Series) Float() []float64 {
	var x []float64
	for _, element := range s.elements {
		x = append(x, element.Float())
	}
	return x
}

// Int 将数据集中的元素作为整数返回，如果数据转换失败自动设为 math.MinInt()
func (s *Series) Int() []int {
	var x []int
	for _, element := range s.elements {
		x = append(x, element.Int())
	}
	return x
}

// Bool 将数据集中的元素作为浮布尔值返回
func (s *Series) Bool() []bool {
	var x []bool
	for _, element := range s.elements {
		x = append(x, element.Bool())
	}
	return x
}

// Any 将数据集中的元素作为任意类型返回
func (s *Series) Any() []any {
	var x []any
	for _, element := range s.elements {
		x = append(x, element.Value())
	}
	return x
}

// Indexes 返回索引切片
func (s *Series) Indexes() []int {
	return s.indexes
}

// RelationalOperator 关系运算
type RelationalOperator int

// ArithmeticOperator 算术运算
type ArithmeticOperator int

const (
	// Equal 相等
	Equal RelationalOperator = iota
	// NotEqual 不相等
	NotEqual
	// LessThan 小于
	LessThan
	// LessOrEqual 小于等于
	LessOrEqual
	// GreaterThan 大于
	GreaterThan
	// GreaterOrEqual 大于等于
	GreaterOrEqual
	// Contains 包含
	Contains
	// StartsWith 以...开始
	StartsWith
	// EndsWith 以...结束
	EndsWith
	// In 在...列表里
	In
	// NotIn 不在...列表里
	NotIn
)

const (
	// Addition 加
	Addition ArithmeticOperator = iota
	// Subtraction 减
	Subtraction
	// Multiplication 乘
	Multiplication
	// Division 除
	Division
	// Remainder 求于
	Remainder
)

// Filter 过滤数据集
func (s *Series) Filter(operator RelationalOperator, values any) (*Series, error) {
	if values == nil {
		return nil, fmt.Errorf("输入值不能为 nil")
	}

	v := reflect.ValueOf(values)
	vKind := v.Kind()
	vType := v.Type()

	// 获取目标类型
	var expectedValueType reflect.Type
	switch s.t {
	case String:
		expectedValueType = reflect.TypeOf("")
	case Int:
		expectedValueType = reflect.TypeOf(0)
	case Float:
		expectedValueType = reflect.TypeOf(0.0)
	case Bool:
		expectedValueType = reflect.TypeOf(true)
	default:
		return nil, fmt.Errorf("不支持的数据类型: %v", s.t)
	}

	// 检查输入类型是否匹配
	if vKind == reflect.Slice && vType.Elem().Kind() == reflect.Interface {
		return nil, fmt.Errorf("输入切片元素类型与数据集类型不同")
	} else if vType != expectedValueType {
		return nil, fmt.Errorf("输入数据类型与数据集类型不同")
	}

	// 操作符合法性检查
	opAllowed, ok := isOperatorAllowed(s.t, operator)
	if !ok || !opAllowed {
		return nil, fmt.Errorf("%s 类型无法执行操作%d", s.t, operator)
	}

	// 特殊操作符参数检查
	if (operator == In || operator == NotIn) && vKind != reflect.Slice {
		return nil, fmt.Errorf("in / NotIn 需要输入切片作为参数")
	}

	// 执行过滤
	_, indexes := filter(
		s.elements,
		func(element Element) bool {
			return evaluateCondition(element, operator, values, vKind)
		},
	)

	if subSet, err := s.SubSet(indexes...); err != nil {
		return nil, err
	} else {
		return subSet, nil
	}
}

// 判断操作符是否允许
func isOperatorAllowed(t Type, op RelationalOperator) (bool, bool) {
	typeOpMap := map[Type][]RelationalOperator{
		String: {Equal, NotEqual, Contains, StartsWith, EndsWith, In, NotIn},
		Int:    {Equal, NotEqual, LessThan, LessOrEqual, GreaterThan, GreaterOrEqual, In, NotIn},
		Float:  {Equal, NotEqual, LessThan, LessOrEqual, GreaterThan, GreaterOrEqual, In, NotIn},
		Bool:   {Equal, NotEqual},
	}
	allowedOps, exists := typeOpMap[t]
	if !exists {
		return false, false
	}
	return slices.Contains(allowedOps, op), true
}

// 根据操作符评估条件
func evaluateCondition(element Element, op RelationalOperator, values any, valuesKind reflect.Kind) bool {
	switch op {
	case Equal:
		return reflect.DeepEqual(element.Value(), values)
	case NotEqual:
		return !reflect.DeepEqual(element.Value(), values)
	case LessThan, LessOrEqual, GreaterThan, GreaterOrEqual:
		if element.dType() == string(Float) && math.IsNaN(element.Float()) {
			return false
		}
		if valuesKind == reflect.Int {
			val := values.(int)
			switch op {
			case LessThan:
				return element.Int() < val
			case LessOrEqual:
				return element.Int() <= val
			case GreaterThan:
				return element.Int() > val
			case GreaterOrEqual:
				return element.Int() >= val
			default:
				return false
			}
		} else {
			val := values.(float64)
			switch op {
			case LessThan:
				return element.Float() < val
			case LessOrEqual:
				return element.Float() <= val
			case GreaterThan:
				return element.Float() > val
			case GreaterOrEqual:
				return element.Float() >= val
			default:
				return false
			}
		}
	case 6:
		return strings.Contains(element.Records(), values.(string))
	case 7:
		return strings.HasPrefix(element.Records(), values.(string))
	case 8:
		return strings.HasSuffix(element.Records(), values.(string))
	case 9, 10:
		v := reflect.ValueOf(values)
		newValues := make([]any, v.Len())
		for i := 0; i < v.Len(); i++ {
			newValues[i] = v.Index(i).Interface()
		}
		found := slices.Contains(newValues, element.Value())
		if op == 9 {
			return found
		} else {
			return !found
		}
	default:
		return false
	}
}

// NewElements 生成 Elements 接口的对象
func NewElements(t Type, l int) []Element {
	var ne []Element
	for i := 0; i < l; i++ {
		var x Element
		switch t {
		case String:
			x = new(stringElement)
		case Int:
			x = new(intElement)
		case Float:
			x = new(floatElement)
		case Bool:
			x = new(boolElement)
		}
		ne = append(ne, x)
	}
	return ne
}

// Arithmetic 算术运算
func (s *Series) Arithmetic(operator ArithmeticOperator, x Series) (*Series, error) {
	if s.t == Bool || x.t == Bool {
		return nil, fmt.Errorf("布尔值不支持算术运算")
	}
	if s.Len() != x.Len() {
		return nil, fmt.Errorf("长度不相等！")
	}
	if operator != 0 && (s.t == String || x.t == String) {
		return nil, fmt.Errorf("字符串不支持该操作")
	}

	ns := &Series{Name: s.Name}
	// 保证结果准确
	if s.t == String || x.t == String {
		ns.t = String
	} else if s.t == Float || x.t == Float {
		ns.t = Float
	} else {
		if operator == 3 {
			ns.t = Float
		} else {
			ns.t = Int
		}
	}
	ns.elements = NewElements(ns.t, s.Len())

	switch operator {
	case 0:
		for i := 0; i < s.Len(); i++ {
			if ns.t == String {
				ns.Element(i).Set(s.Element(i).Records() + x.Element(i).Records())
			} else {
				f, _ := decimal.NewFromFloat(s.Element(i).Float()).
					Add(decimal.NewFromFloat(x.Element(i).Float())).Float64()
				ns.Element(i).Set(f)
			}
		}
	case 1:
		for i := 0; i < s.Len(); i++ {
			f, _ := decimal.NewFromFloat(s.Element(i).Float()).
				Sub(decimal.NewFromFloat(x.Element(i).Float())).Float64()
			ns.Element(i).Set(f)
		}
	case 2:
		for i := 0; i < s.Len(); i++ {
			f, _ := decimal.NewFromFloat(s.Element(i).Float()).
				Mul(decimal.NewFromFloat(x.Element(i).Float())).Float64()
			ns.Element(i).Set(f)
		}
	case 3:
		for i := 0; i < s.Len(); i++ {
			if x.Element(i).Float() == 0 {
				ns.Element(i).Set(math.NaN())
			} else {
				f, _ := decimal.NewFromFloat(s.Element(i).Float()).
					Div(decimal.NewFromFloat(x.Element(i).Float())).Float64()
				ns.Element(i).Set(f)
			}
		}
	case 4:
		for i := 0; i < s.Len(); i++ {
			if x.Element(i).Float() == 0 {
				ns.Element(i).Set(math.NaN())
			} else {
				f, _ := decimal.NewFromFloat(s.Element(i).Float()).
					Mod(decimal.NewFromFloat(x.Element(i).Float())).Float64()
				ns.Element(i).Set(f)
			}
		}
	}
	return ns, nil
}

// Reset 重置 Series
func (s *Series) Reset() {
	// 删除所有元素
	s.elements = nil
	s.indexes = nil
	s.t = ""
	s.Name = ""
}

func filter[T comparable](slice []T, condition func(T) bool) ([]T, []int) {
	var filtered []T
	var indexes []int
	for index, item := range slice {
		if condition(item) {
			filtered = append(filtered, item)
			indexes = append(indexes, index)
		}
	}
	return filtered, indexes
}
